import math, datetime

product_list = {
    # Fresh Produce
    'Apples':2, 
    'Bananas':1, 
    'Eggs': 5,

    # Beverages
    'Water': 5, 
    'Juice': 4,

    # Household
    'Toilet paper': 4,
    'Shampoo': 5,
    'Cutting knife': 10,

    # Dessert
    'Ice-cream': 10, 
    'Cake': 20,
    
    # Liquor
    'Wine': 50,
    'Beer': 25,

    'Zero Alcohol': 20,
    'Cosmetic fillers': 20
}

liquor_list = ['Wine', 'Beer']

weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
weekends = ['Saturday', 'Sunday']

def is_age_valid(age):
    """
    To check if the user's age is valid for shopping.
    
    :param age: int(Customer's age and should be greater than 0, and less than or equal to 120.)
    :return: bool(Returns True if the age input is valid, otherwise False.)
    
    This function is used to check if the age entered by the customer is valid (age must be greater than 0 and less than or equal to 120).    
    """
    if isinstance(age,int):
        return 0 < age <= 120
    return False


def is_input_valid(product_name, product_quantity):
     """
     To check if the input product name and quantity is valid for shopping.

     :param product_name: str(The name of the product to be checked (case-sensitive).)
     :param product_quantity: int(The quantity of the product to be purchased, must be at least 1.)
     :return: bool(Returns True if the product name exists and the quantity is valid, otherwise False.)

    This function is used to check if the product name exists in the corresponding product list and if the input product quantity  is greater than or equal to 1.
     """

     if product_name in product_list and product_quantity >= 1:
        return True
     return False

def is_product_eligible(product_name, age, current_dateTime):
    """
    To check if the product is eligible for the customer based on age and time.

    :param product_name: str(The name of the product to be checked, case-sensitive.)
    :param age: int(The customer's age, must meet the age requirements for the product.)
    :param current_dateTime: datetime(The current date and time, used to check time-based restrictions.)
    :return: bool(Returns True if the product is eligible for purchase by the customer, otherwise False.)

    This function is used to check if a product can be sold to a customer based on their age and the current time.
    It follows government regulations regarding alcohol sales and other restricted items.
    """
    # Age restrictions dictionary
    age_limits = {
        'Wine': 18, 'Beer': 18,
        'Cutting knife': 15, 'Cosmetic fillers': 15
    }

    # The age_limits for liquor
    current_day = current_dateTime.strftime('%A')
    hour = current_dateTime.hour
    
    # Check age restrictions for customer
    if product_name in age_limits and age < age_limits[product_name]:
        return False
        
    # Check time restrictions for liquor
    if product_name in ['Wine', 'Beer']:
        hour = current_dateTime.hour

    if current_day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']:
        if not (10 < hour < 15):
            return False

    elif current_day in ['Saturday', 'Sunday']:
        if not ((0 <= hour < 4) or (22 <= hour < 24)):
                return False
     
    return True

def is_product_overlimit(product_name, product_quantity, current_dateTime):
    '''
    To check if the product quantity exceeds the limit during high-demand periods.

    :param product_name: str(The name of the product to be checked)
    :param product_quantity: int(The quantity of the product being purchased.)
    :param current_dateTime: datetime(The current date and time to check restrictions.)
    :return: bool(Returns True if the product quantity exceeds the allowed limit, otherwise False.)

    This function is used to check if the quantity of certain product exceeds the limit based on the product type,
    current date or time during high-demand periods.

    '''

    # Get current day, month, hour
    current_day = current_dateTime.strftime('%A')
    current_month = current_dateTime.month
    current_hour = current_dateTime.hour 
    current_day_of_month = current_dateTime.strftime('%m-%d')

    # Limit for Liquor on Christmas and New Year's purchases
    if product_name in ['Wine', 'Beer']:
        if current_day_of_month in ['12-25', '01-01']:
            if product_quantity > 4:
                return True
            else:
                return False

    # Limit for Liquor on weekdays
    if product_name in ['Wine', 'Beer']:
        if current_day in weekdays and product_quantity > 2:
            return True

    # Product  limits for Toilet paper
    if product_name == 'Toilet paper' and product_quantity > 2:
        return True
    
    
    # Limit for Ice cream
    if product_name == 'Ice-cream' and (current_month == 12 or 1 <= current_month <= 3) and product_quantity > 3:
        return True
    
    # Limit for Fruit
    if product_name in ['Apples', 'Bananas'] and current_hour < 19 and product_quantity > 5:
        return True

    # Limit for Eggs
    if product_name == 'Eggs' and (6 <= current_month <= 7) and product_quantity > 2:
        return True

    return False


def calculate_delivery_charges(distance_km, total_amount, current_day_name):
    '''
    To calculate the delivery charges based on the delivery distance and total amount.

    :param distance_km: float(The distance in kilometers).
    :param total_amount: float(The total amount of the order before discounts).
    :param  current_day_name: str(The name of the current day). 
    :return: float(The calculated delivery charge).

    This function is used to calculate the delivery charge based on the distance. If the distance exceeds 5 km and the total
    amount is at least 50 AUD, a discount is applied based on the day of the week (10% on weekdays, 20% on weekends).
    '''

    delivery_charges = 0.0

    # calculate the original charge based on the distance
    if distance_km < 3:
        delivery_charges = 0.0
    elif 3 <= distance_km <= 5:
        delivery_charges = 2.0
    elif 5 <= distance_km <= 10:
        delivery_charges = 2.0 + (distance_km - 5) * 1.5
    else:
        delivery_charges= distance_km * 1.5

    # calculate the charge after applying the discount
    if distance_km > 5 and total_amount >= 50:
        if current_day_name in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']:
            delivery_charges *= 0.9 # 10% discount
        elif current_day_name in ['Saturday', 'Sunday']:
            delivery_charges *= 0.8 # 20% discount
   
    return round(delivery_charges, 2)

    
def discount_of_the_day(product_name, product_quantity, original_price, current_dateTime):
    '''
    To calculate the discount after the special campaigns today.

    :param product_name: str(The name of the product). 
    :param product_quantity: int(The quantity of the product).
    :param original_price: float(The original price of the product).
    :param current_dateTime: datetime(The current date and time).
    :return: float(The discount amount to be applied). 

    '''

    discount = 0.0
    current_month = current_dateTime.month
    current_hour = current_dateTime.hour
    current_day = current_dateTime.strftime('%A')

    # Shampoo(30% off from june to july)

    if product_name == 'Shampoo' and 6 <= current_month <= 7:
        discount = original_price * 0.3 *product_quantity
    
    # Weekends(Buy 2 get 1 free)
    if product_name == 'Juice' and current_day in ['Saturday', 'Sunday']:
        free_juice = product_quantity // 2
        discount = free_juice * (original_price/product_quantity)
    
    # Weekedays(Apples and Bananas applied 50% discount)
    if product_name in ['Apples', 'Bananas'] and current_hour >= 19 and current_day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']:
        discount = original_price * 0.5 * product_quantity

    # January and December: 50% discount in Juice, Cake and Ice-cream  
    if product_name in ['Juice', 'Cake', 'Ice-cream'] and current_month in [1, 12]:
        discount = original_price * 0.5 * product_quantity

    return discount

def discount_Australia_day(product_name, product_quantity, original_price, current_dateTime):
    '''
    To calculate the discount for Australia Day

    :param product_name: str(The name of the product). 
    :param product_quantity: int(The quantity of the product).
    :param original_price: float(The original price of the product).
    :param current_dateTime: datetime(The current date and time).
    :return: float(The discount amount to be applied). 

    '''

    # Check if the day is Australia Day
    if current_dateTime.strftime('%m-%d') != '01-26':
        return 0.0
    
    discount = 0.0

    # Customer can collect a free cake on Australia Day
    if product_name == 'Cake':
        discount += original_price * min(product_quantity, 1)

    # Buy 10 eggs, get 5 free
    if product_name == 'Eggs' and product_quantity >= 10:
        free_eggs = 5
        discount += free_eggs * (original_price/product_quantity)

    # Buy 3 bottles of Water, get 1 free
    if product_name == 'Water' and product_quantity >= 3:
        free_water = product_quantity // 3
        discount += free_water * (original_price/product_quantity)

    # Bananas applied 50% discount on that day
    if product_name == 'Bananas':
        discount += original_price * 0.5 * product_quantity

    # Beer: Buy 1 get 1 Free
    if product_name == 'Beer':
        free_beer = product_quantity // 2
        discount += free_beer * original_price

    return discount   
